package com.wyman.factory.xml;

import com.wyman.BeanException;
import com.wyman.PropertyValue;
import com.wyman.factory.config.BeanDefinition;
import com.wyman.factory.config.BeanReference;
import com.wyman.core.io.Resource;
import com.wyman.core.io.ResourceLoader;

import java.io.IOException;
import java.io.InputStream;

import cn.hutool.core.util.XmlUtil;
import com.wyman.factory.support.AbstractBeanDefinitionReader;
import com.wyman.factory.support.BeanDefinitionRegistry;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import cn.hutool.core.util.StrUtil;

/**
 * @Author 李伟民
 * @Description
 * @DateTime 2024/12/6 02:06
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
        super(beanDefinitionRegistry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry, ResourceLoader resourceLoader){
        super(beanDefinitionRegistry,resourceLoader);

    }

    @Override
    public void loadBeanDefinitions(Resource resource) {
      try(InputStream inputStream =  resource.getInputStream()){

          doLoadBeanDefinitions(inputStream);

      } catch (IOException | ClassNotFoundException e) {
          throw new BeanException("IOException parsing XML document from " + resource, e);
      }

    }

    @Override
    public void loadBeanDefinitions(Resource[] resources) {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(String[] locations){
        for (String location : locations) {
            loadBeanDefinitions(location);
        }
    }

    private void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {

        Document document = XmlUtil.readXML(inputStream);
        Element root = document.getDocumentElement();

        NodeList childNodes = root.getChildNodes();
        for(int i=0;i<childNodes.getLength();i++){

            if(!(childNodes.item(i) instanceof Element)){
                continue;
            }
            //拿到了一个bean元素
            Element bean = (Element) childNodes.item(i);
            //如果bean不是Element实例或者xml文档中标签不是bean 那直接处理下一个节点
            if(!("bean".equals(bean.getNodeName()))){
                continue;
            }
            // id和name都可以作为bean的名字 id优先级更高
            String id = bean.getAttribute("id");
            String name = bean.getAttribute("name");

            String className = bean.getAttribute("class");
            Class<?> clazz = Class.forName(className);
            // id和name都可以作为bean的名字 id优先级更高
            String beanName = StrUtil.isNotEmpty(id)?id:name;
            //如果没有设置名字 那么默认为类名首字母小写
            if(StrUtil.isEmpty(beanName)){
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }
            //定义bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);

            //读取属性并填充
            NodeList beanChildNodes = bean.getChildNodes();
            for(int j=0;j<beanChildNodes.getLength();j++){
                if(!(beanChildNodes.item(j) instanceof Element)){
                    continue;
                }
                Element property = (Element) beanChildNodes.item(j);
                if(!("property".equals(property.getNodeName()))){
                    continue;
                }
                String attrName = property.getAttribute("name");
                String attrValue = property.getAttribute("value");
                String attrRef = property.getAttribute("ref");
                Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;

                PropertyValue propertyValue = new PropertyValue(attrName,value);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }

            //需要判断bean是否重复
            if(getRegistry().containsBeanDefinition(beanName)){
                throw new BeanException("beanName duplicate : "+ beanName);
            }
            getRegistry().registerBeanDefinition(beanName,beanDefinition);
        }
    }
}
